/*************************************************************************
	> File Name: 1.server.c
	> Author: Maureen 
	> Mail: 1437867946@qq.com 
	> Created Time: Sun 09 May 2021 12:54:23 PM CST
 ************************************************************************/

#include "head.h"

// ./server 20001
//模拟粘包和拆包问题

#define MAX 20

struct UserInfo {
    int fd;
    char ip[20];
    char name[20];
};

struct Packet {
    char filename[50]; //文件名
    ssize_t size; //文件大小
    char body[1024]; //报文
};

//接收客户端发送的文件
int recv_file(int sockfd, const char *datadir) { 
    //完整模拟ppt中的粘包和拆包过程
    struct Packet packet, packet_t, packet_pre;
    size_t packet_size = sizeof(struct Packet);
    size_t offset = 0, recv_size = 0, cnt = 0, total_size = 0;
    FILE *fp;
    if (mkdir(datadir, 0755) < 0) { //创建目录
        if (errno == EEXIST) {
            printf("Dir Exist!\n");
        } else {
            return -1;
        }
    };
        
    while (1) {
        if (offset) { //考虑粘包
            memcpy((char *)&packet, &packet_pre, offset);
        }
        while ((recv_size = recv(sockfd, (void *)&packet_t, packet_size, 0)) > 0) {
            //判断是否为一个整包
            if (offset + recv_size == packet_size) {
                printf("整包!\n");
                memcpy((void *)&packet + offset, &packet_t, recv_size);
                offset = 0;
                break;
            } else if (offset + recv_size < packet_size) { //收到的数据没有构成一个整包
                printf("拆包!\n");
                memcpy((char *)&packet + offset, &packet_t, recv_size);
                offset += recv_size;
            } else { //收到的数据大于一个包
                printf("粘包!\n");
                memcpy((char *)&packet + offset, &packet_t, packet_size - offset);
                memcpy((char *)&packet_pre, (char *)&packet_t + (packet_size - offset), recv_size - (packet_size - offset));
                offset = recv_size - (packet_size - offset);
                break;
            }
        }
        //收齐了一个包
        if (cnt == 0) {
            //创建文件
            printf("Saving file <%s> to ./%s/...\n", packet.filename, datadir);
            char filepath[512] = {0};
            sprintf(filepath, "./%s/%s", datadir, packet.filename);
            if ((fp = fopen(filepath, "wb")) == NULL) {
                perror("fopen");
                return -1;
            }
        }
        cnt++;
        //将packet中的内容写到创建的文件中
        //需要考虑到：如果最后接收到的数据并不是一个整包，那么只能读数据大小而不是包的大小，否则会有多余的数据
        size_t write_size;
        if (packet.size - total_size >= packet_size) {
            write_size = fwrite(packet.body, 1, sizeof(packet.body), fp);
        } else {
            write_size = fwrite(packet.body, 1, packet.size - total_size, fp);
        }
        total_size += write_size;
        if (total_size >= packet.size) {
            printf("File Finished!\n");
            break;
        }
    }
    fclose(fp);
    return 0;
}

void *do_work(void *arg) { //线程处理函数
    struct UserInfo *user;
    user = (struct UserInfo *)arg;

    printf("Waiting for data : <%s:%s>\n", user->ip, user->name); 
    recv_file(user->fd, user->name);
    close(user->fd);
    return NULL;
}

int main(int argc, char **argv) {
    int server_listen, port, sockfd; //sockfd是新连接
    pthread_t tid[MAX + 5]; //线程id
    struct UserInfo users[MAX + 5];
    bzero(&users, sizeof(users));
    if (argc != 2) {
        fprintf(stderr, "Usage : %s port!\n", argv[0]);
        exit(1);
    }
    port = atoi(argv[1]);
    if ((server_listen = socket_create(port)) < 0) {
        perror("server_listen"); //此处能用perror，是因为socket_create函数中涉及到的函数出错都会设置errorno, perror可以打印出这些错误
        exit(1);
    }
    printf("After socket_create<%d>\n", server_listen);
    //线程的并发模型
    while (1) {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        if ((sockfd = accept(server_listen, (struct sockaddr *)&client, &len)) < 0) {
            perror("accept");
            exit(1);
        }
        if (recv(sockfd, &users[sockfd].name, sizeof(users[sockfd].name), 0) <= 0) {
            close(sockfd);
            continue;
        } else { //接收数据成功,获取name和ip
            users[sockfd].fd = sockfd;
            strcpy(users[sockfd].ip, inet_ntoa(client.sin_addr));
        }
        int ack = 1;
        //收到name后回复一个ack,发送方就会接收到ack
        send(sockfd, (void *)&ack, sizeof(int), 0); //此处没有判断send是否成功，是因为刚刚收到数据，所以认为是通的
        //创建线程
        pthread_create(&tid[sockfd], NULL, do_work, (void *)&users[sockfd]);
    }
    return 0;
}

